package data.result;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import core.SemanticMatch;
import data.cad.CAD;
import data.component.BoundingBox;
import data.component.Wall;
import data.xml.XML;
import org.apache.commons.io.FileUtils;
import utils.Matrix;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;


public class ScanResult {

    /**
     * 数据版本号
     */
    public String version;

    public String unreTaskId;

    /**
     * 本次测量是否存在爆点，"1":不存在，"2":存在
     */
    public String ispass;

    /**
     * 设备ID
     */
    public String deviceId;

    /**
     * 设备版本号
     */
    public String deviceVersion;

    /**
     * 测量数据
     */
    public MeasureData data;

    /**
     * 等高线数据
     */
    public ArrayList<Object> files = new ArrayList<Object>();

    /**
     * 添加模糊匹配相关
     */
    public Double credibility;

    /**
     * 设备与站点的实际距离(单位mm)
     */
    public Double device_position_offset;

    /**
     * XML2CAD 4x4坐标变换矩阵
     */
    public ArrayList<Double> matrix;

    /**
     * CAD中的墙与XML中的墙的映射关系
     */
    public HashMap<String, Object> mapping = new HashMap<String, Object>();

    /**
     * CAD中的墙的信息（用于app绘制手动匹配界面）
     */
    public HashMap<String, Object> drawing_outline = new HashMap<String, Object>();

    /**
     * XML中的墙的信息（用于app绘制手动匹配界面）
     */
    public HashMap<String, Object> scan_roomContour = new HashMap<String, Object>();

    /**
     * CAD中最长的墙（用于手动匹配界面高亮）
     */
    public HashMap<String, Object> bestForMatch = new HashMap<String, Object>();

    /**
     * 推荐站点
     */
    public ArrayList<HashMap<String, Object>> recommendedScanStation = new ArrayList<HashMap<String, Object>>();

    /**
     * 通过房间ID替换站点ID后，MD5生成
     */
    public String mergeId;

    /**
     * 手动添加站点，获取房间ID
     */
    public String room;

    /**
     * 站点ID
     */
    public String pointId;

    /**
     * 任务时间戳（内部使用，上传后时间戳更新）
     */
    public String taskTimestamp;

    /**
     * mergeId时间戳（内部使用，上传后时间戳更新）
     */
    public String mergeTimestamp;

    /**
     * 站点坐标
     */
    public String pointCoordinate;

    public static ScanResult GetScanResult(String path) {
        File file = new File(path);
        String fileString = null;
        try {
            fileString = FileUtils.readFileToString(file, "utf-8");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return JSON.parseObject(fileString, ScanResult.class);

    }

    private void SetMapping(HashMap<String, Wall> map) {
        var id2Dict = new HashMap<String, Object>();
        for (var key : map.keySet()) {
            var wall = map.get(key);
            if (wall == null) continue;
            var wallDict = new HashMap<String, Double>() {
                {
                    put("id", Double.valueOf(wall.id));
                    put("type", null);
                    put("material", null);
                    put("scan_quality", wall.scanQuality);
                    put("length", wall.length);
                    put("StartX", wall.start[0]);
                    put("StartY", wall.start[1]);
                    put("EndX", wall.end[0]);
                    put("EndY", wall.end[1]);
                }

            };
            id2Dict.put(key, wallDict);
        }
        this.mapping = id2Dict;
    }

    private void SetDrawingOutLine(CAD cad) {
        var cadAttrs = new HashMap<String, Object>();
        var wallAttrs = new ArrayList<Object>();

        for (var wall : cad.walls) {
            var line = new ArrayList<>(Arrays.asList(wall.start, wall.end));
            var lines = new ArrayList<>(List.of(line));
            var wallAttr = new HashMap<String, Object>() {
                {
                    put("id", wall.id);
                    put("lines", lines);
                }
            };
            wallAttrs.add(wallAttr);
        }

        cadAttrs.put("walls", wallAttrs);

        cadAttrs.put("doors", new ArrayList<Object>());

        cadAttrs.put("scan_point", cad.station.location);

        this.drawing_outline = cadAttrs;
    }

    private void SetScanRoomContour(XML xml, Matrix m) {
        var xmlAttrs = new HashMap<String, Object>();
        var wallAttrs = new ArrayList<HashMap<String, Double>>();
        for (var wall : xml.walls) {
            var start = m.Transform(wall.start);
            var end = m.Transform(wall.end);
            var wallAttr = new HashMap<String, Double>() {
                {
                    put("id", Double.valueOf(wall.id));
                    put("StartX", start[0]);
                    put("StartY", start[1]);
                    put("EndX", end[0]);
                    put("EndY", end[1]);
                }

            };
            wallAttrs.add(wallAttr);
        }
        xmlAttrs.put("wall", wallAttrs);

        var opAttrs = new ArrayList<HashMap<String, Double>>();
        for (var op : xml.openings) {
            var start = m.Transform(op.start);
            var end = m.Transform(op.end);
            var opAttr = new HashMap<String, Double>() {
                {
                    put("StartX", start[0]);
                    put("StartY", start[1]);
                    put("EndX", end[0]);
                    put("EndY", end[1]);
                }
            };
            opAttrs.add(opAttr);
        }

        xmlAttrs.put("door", opAttrs);

        xmlAttrs.put("window", new ArrayList<Object>());

        xmlAttrs.put("scan_point", m.Transform(new Double[]{0d, 0d, 0d}));

        this.scan_roomContour = xmlAttrs;
    }

    private void SetMatrix(Matrix m) {
        this.matrix = m.ToArrayList();
    }

    private void SetCredibility(double cred) {
        this.credibility = cred;
    }

    private void SetRoom(String roomId) {
        this.room = roomId;
    }

    private void SetMeasurementData(SemanticMatch match, CAD cad, XML xml) {
        data.SetMeasureData(cad,xml, match.mapping, match.matrix, xml.zRotate);
    }

    public void CompleteFields(SemanticMatch match, CAD cad, XML xml)
    {
        SetMapping(match.mapping);

        SetDrawingOutLine(cad);

        SetScanRoomContour(xml, match.matrix);

        SetMatrix(match.matrix);

        SetCredibility(match.credibility);

        SetRoom(cad.room.id);

        SetMeasurementData(match, cad, xml);


    }


    public void Save(String filePath )
    {
        this.RenameOldResult(filePath);
        String content = JSON.toJSONString(this);
        try {
            File file = new File(filePath);
            // 创建文件
            file.createNewFile();
            // 写入文件
            Writer write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
            write.write(content);
            write.flush();
            write.close();
        } catch (Exception e) {
            e.printStackTrace();

        }
    }

    private void RenameOldResult(String filePath)
    {
        File oldFile = new File(filePath );
        File newFile = new File(filePath+"_bak.json" );
        //重命名
        System.out.println(oldFile.renameTo(newFile));
    }
}
